package model;

import java.util.ArrayList;
import java.util.Date;

import org.workplicity.entry.Entry;
import org.workplicity.util.Helper;
import org.workplicity.util.WorkDate;
import org.workplicity.worklet.WorkletContext;

import com.mongodb.BasicDBObject;
import com.mongodb.util.JSON;

import cc.test.bridge.BridgeConstants.Side;
import cc.test.bridge.BudgetInterface;
import cc.test.bridge.LineInterface;
import cc.test.bridge.NoteInterface;
import cc.test.bridge.SublineInterface;
import cc.test.bridge.TransactionInterface;

public class Budget extends Entry implements BudgetInterface
{
	public enum Roll
	{
		DAILY, BIWEEKLY, WEEKLY, BIMONTHLY, MONTHLY, BIANNUALLY, QUARTERLY, ANNUALLY
	};

	public enum Status
	{
		DRAFT, REVIEW, FINAL
	};

	protected Integer budgetId = 0;
	protected String name = "N/A";
	protected String description = "N/A";
	protected ArrayList<Integer> lineIds = new ArrayList<Integer>();
	protected ArrayList<NoteInterface> notes = new ArrayList<NoteInterface>();
	protected Date startDate = new WorkDate();
	protected Date endDate = new WorkDate();
	protected Roll roll = Roll.WEEKLY;
	private final String repositoryName = "Budgets";
	private static final long serialVersionUID = 1L;
	protected Status status = Status.DRAFT;

	/**
	 * Default no argument constructor
	 */
	public Budget()
	{
		Hampers.getBudgetCreates().put(-1, this);
	}
	

	@Override
	/**
	 * Getter for name
	 * 
	 * @return An String containing the name
	 */
	public String getName()
	{
		return this.name;
	}

	@Override
	/**
	 * Setter for name
	 * 
	 * @param name The String containing name
	 */
	public void setName(String name)
	{
		this.name = name;
		
		// If created already (it is not in the create hamper)
		if (this.id != -1)
		{
			// If BudgetUpdates hamper does not contain the budget already
			if (!Hampers.getBudgetUpdates().containsKey(this.id))
			{
				Hampers.getBudgetUpdates().put(this.id, this);
			}
		}
	}
	
	@Override
	/**
	 * Getter for description
	 * 
	 * @return An String containing the description
	 */
	public String getDescription()
	{
		return this.description;
	}

	@Override
	/**
	 * Setter for description
	 * 
	 * @param des The String containing description
	 */
	public void setDescription(String des)
	{
		this.description = des;
		
		// If created already (it is not in the create hamper)
		if (this.id != -1)
		{
			// If BudgetUpdates hamper does not contain the budget already
			if (!Hampers.getBudgetUpdates().containsKey(this.id))
			{
				Hampers.getBudgetUpdates().put(this.id, this);
			}
		}
	}

	/**
	 * Getter for lineIds
	 * 
	 * @return An ArrayList<Integer> containing the lineIds
	 */
	public ArrayList<Integer> getLineIds()
	{
		return lineIds;
	}

	/**
	 * Setter for lineIds
	 * 
	 * @param lineIds The ArrayList<Integer> containing lineIds
	 */
	public void setLineIds(ArrayList<Integer> lineIds)
	{
		this.lineIds = lineIds;
		
		// If created already (it is not in the create hamper)
		if (this.id != -1)
		{
			// If BudgetUpdates hamper does not contain the budget already
			if (!Hampers.getBudgetUpdates().containsKey(this.id))
			{
				Hampers.getBudgetUpdates().put(this.id, this);
			}
		}
	}

	/**
	 * Getter for endDate
	 * 
	 * @return An Date containing the endDate
	 */
	public Date getEndDate()
	{
		return endDate;
	}

	/**
	 * Setter for endDate
	 * 
	 * @param endDate The Date containing endDate
	 */
	public void setEndDate(Date endDate)
	{
		this.endDate = endDate;

		// If created already (it is not in the create hamper)
		if (this.id != -1)
		{
			// If BudgetUpdates hamper does not contain the budget already
			if (!Hampers.getBudgetUpdates().containsKey(this.id))
			{
				Hampers.getBudgetUpdates().put(this.id, this);
			}
		}
	}

	/**
	 * Getter for startDate
	 * 
	 * @return An Date containing the startDate
	 */
	public Date getStartDate()
	{
		return startDate;
	}

	/**
	 * Setter for startDate
	 * 
	 * @param startDate The Date containing startDate
	 */
	public void setStartDate(Date startDate)
	{
		this.startDate = startDate;
		
		// If created already (it is not in the create hamper)
		if (this.id != -1)
		{
			// If BudgetUpdates hamper does not contain the budget already
			if (!Hampers.getBudgetUpdates().containsKey(this.id))
			{
				Hampers.getBudgetUpdates().put(this.id, this);
			}
		}
	}

	/**
	 * Getter for notes
	 * 
	 * @return An ArrayList<NoteInterface> containing the notes
	 */
	public ArrayList<NoteInterface> getNotes()
	{
		return notes;
	}

	/**
	 * Setter for notes
	 * 
	 * @param notes The ArrayList<NoteInterface> containing notes
	 */
	public void setNotes(ArrayList<NoteInterface> notes)
	{
		this.notes = notes;

		// If created already (it is not in the create hamper)
		if (this.id != -1)
		{
			// If BudgetUpdates hamper does not contain the budget already
			if (!Hampers.getBudgetUpdates().containsKey(this.id))
			{
				Hampers.getBudgetUpdates().put(this.id, this);
			}
		}
	}

	/**
	 * Getter for roll
	 * 
	 * @return An Roll containing the roll
	 */
	public Roll getRoll()
	{
		return roll;
	}

	/**
	 * Setter for roll
	 * 
	 * @param roll The Roll containing roll
	 */
	public void setRoll(Roll roll)
	{
		this.roll = roll;
		
		// If created already (it is not in the create hamper)
		if (this.id != -1)
		{
			// If BudgetUpdates hamper does not contain the budget already
			if (!Hampers.getBudgetUpdates().containsKey(this.id))
			{
				Hampers.getBudgetUpdates().put(this.id, this);
			}
		}
	}

	@Override
	/**
	 * Getter for repositoryName
	 * 
	 * @return An String containing the repositoryName
	 */
	public String getRepositoryName()
	{
		return repositoryName;
	}

	/**
	 * Setter for repositoryName
	 * 
	 * @param name The String containing repositoryName
	 */
	public void setRepositoryName(String name)
	{
	}

	/**
	 * Getter for status
	 * 
	 * @return An Status containing the status
	 */
	public Status getStatus()
	{
		return status;
	}

	/**
	 * Setter for status
	 * 
	 * @param status The Status containing status
	 */
	public void setStatus(Status status)
	{
		this.status = status;
		if (this.id != -1)
		{
			// If BudgetUpdates hamper does not contain the budget already
			if (!Hampers.getBudgetUpdates().containsKey(this.id))
			{
				Hampers.getBudgetUpdates().put(this.id, this);
			}
		}
	}

	@Override
	/**
	 * Commits a dirty budget to the repository, as well as any associated lines.
	 */
	public Boolean commit()
	{
		// ------Commit Creates--------
		Boolean successful = true;
		successful = Transaction.commitTransactionCreates();
		successful = Subline.commitSublineCreates();
		successful = Line.commitLineCreates();

		for (BudgetInterface budget : Hampers.getBudgetCreates().values())
		{
			// insert line into Mongo
			try
			{
				successful = Helper.insert((Budget) budget,
						budget.getRepositoryName(),
						
						WorkletContext.getInstance());
				if (successful)
				{
					System.out.println("Created budget with id "
							+ budget.getId());
				}
				else
				{
					System.out.println("Could not create budget with id "
							+ budget.getId());
					
					return false;
				}
			}
			catch (Exception e)
			{
				System.out.println("Error: Could not create budget with id "
						+ budget.getId());
				
				e.printStackTrace();
				successful = false;
			}
			
			// Give new Id to all line children (if any)
			if (((Budget) budget).getLineIds().size() > 0)
			{
				for (Integer i : ((Budget) budget).getLineIds())
				{
					if (Hampers.getLineUpdates().containsKey(i))
					{
						((Line) Hampers.getLineUpdates().get(i))
								.setBudgetId(id);
					}
				}
			}
		}
		
		Hampers.clearCreates();

		// ----------Commit Deletes------------
		for (TransactionInterface trans : Hampers.getTransDeletes().values())
		{
			successful = successful && Transaction
					.commitTransactionDeletes((Transaction) trans);
		}
		
		for (SublineInterface sub : Hampers.getSublineDeletes().values())
		{
			successful = successful && Subline.commitSublineDeletes((Subline) sub);
		}
		
		for (LineInterface line : Hampers.getLineDeletes().values())
		{
			successful = successful && Line.commitLineDeletes((Line) line);
		}
		
		for (BudgetInterface budget : Hampers.getBudgetDeletes().values())
		{
			// update budget in Mongo
			try
			{
				successful = successful && Helper.delete((Budget) budget,
						budget.getRepositoryName(),
						WorkletContext.getInstance());
				
				if (successful)
				{
					System.out.println("Deleted budget with id "
							+ budget.getId());
				}
				else
				{
					System.out.println("Could not delete budget with id "
							+ budget.getId());
					
					return false;
				}
			}
			catch (Exception e)
			{
				System.out.println("Error: Could not delete budget with id "
						+ budget.getId());
				
				e.printStackTrace();
				successful = false;
			}
		}
		
		Hampers.clearDeletes();

		// -----------Commit Updates--------------
		successful = Transaction.commitTransactionUpdates();
		successful = successful && Subline.commitSublineUpdates();
		successful = successful && Line.commitLineUpdates();
		
		for (BudgetInterface budget : Hampers.getBudgetUpdates().values())
		{
			// update budget in Mongo
			try
			{
				successful = successful && Helper.insert((Budget) budget,
						budget.getRepositoryName(),
						WorkletContext.getInstance());
				
				if (successful)
				{
					System.out.println("Updated budget with id "
							+ budget.getId());
				}
				else
				{
					System.out.println("Could not update budget with id "
							+ budget.getId());
					
					return false;
				}
			}
			catch (Exception e)
			{
				System.out.println("Error: Could not update budget with id "
						+ budget.getId());
				
				e.printStackTrace();
				successful = false;
			}
		}
		
		Hampers.clearUpdates();

		return successful;
	}

	@Override
	/**
	 * Adds a Note to the Budget
	 * 
	 * @param note The NoteInterface to add to a Budget
	 */
	public void add(NoteInterface note)
	{
		this.notes.add(note);

		// If created already (it is not in the create hamper)
		if (this.id != -1)
		{
			// If BudgetUpdates hamper does not contain the budget already
			if (!Hampers.getBudgetUpdates().containsKey(this.id))
			{
				Hampers.getBudgetUpdates().put(this.id, this);
			}
		}
	}

	@Override
	/**
	 * Adds a Line to the Budget
	 * 
	 * @param line The LineInterface to add to a Budget
	 */
	public void add(LineInterface line)
	{
		if (this.id == -1)
		{
			Integer uid = (int) System.currentTimeMillis();
			((Line) line).setBudgetId(uid);
			
			Hampers.getBudgetUpdates().put(uid, this);
			Hampers.getLineCreates().put(-1, line);
		}
		else
		{
			Hampers.getBudgetUpdates().put(this.id, this);
			Hampers.getLineCreates().put(-1, line);
		}
	}

	@Override
	/**
	 * Creates a new Line
	 * 
	 * @return A new Line whose parentLineId is this Budget's id
	 */
	public LineInterface createLine()
	{
		return new Line(this.id);
	}

	@Override
	/**
	 * Creates a new Note
	 * 
	 * @return A new Note
	 */
	public NoteInterface createNote()
	{
		return new Note();
	}
	
	@Override
	/**
	 * Deletes a Line from the Budget
	 * 
	 * @param line The LineInterface to delete
	 */
	public void delete(LineInterface line)
	{
		Hampers.getLineDeletes().put(((Line) line).getId(), line);

		// If created already (it is not in the create hamper)
		if (this.id != -1)
		{
			// If BudgetUpdates hamper does not contain the budget already
			if (!Hampers.getBudgetUpdates().containsKey(this.id))
			{
				Hampers.getBudgetUpdates().put(this.id, this);
			}
		}
	}

	@Override
	/**
	 * Deletes a Note from the Budget
	 * 
	 * @param note The NoteInterface to delete
	 */
	public void delete(NoteInterface note)
	{
		this.notes.remove(note);

		// If created already (it is not in the create hamper)
		if (this.id != -1)
		{
			// If BudgetUpdates hamper does not contain the budget already
			if (!Hampers.getBudgetUpdates().containsKey(this.id))
			{
				Hampers.getBudgetUpdates().put(this.id, this);
			}
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	/**
	 * Fetches the Lines that belong to the Budget
	 * 
	 * @return An ArrayList<LineInterface> containing the LineInterfaces
	 *         that were fetched.
	 */
	public ArrayList<LineInterface> fetchLines(Side side)
	{
		ArrayList<LineInterface> lines = new ArrayList<LineInterface>();

		for (Integer lineId : this.lineIds)
		{
			BasicDBObject predicate = new BasicDBObject();
			predicate.put("entry.id", lineId);
			String criteria = JSON.serialize(predicate);
			
			ArrayList<LineInterface> ls = Helper.query("Lines", criteria,
					WorkletContext.getInstance());
			
			for (LineInterface l : ls)
			{
				lines.add(l);
			}
		}
		
		return lines;
	}

	@Override
	/**
	 * Fetches the Notes that belong to the Budget
	 * 
	 * @return An ArrayList<NoteInterface> containing the NoteInterfaces
	 *         that were fetched.
	 */
	public ArrayList<NoteInterface> fetchNotes()
	{
		return this.notes;
	}

	@Override
	/**
	 * Adds the budget to the update hamper if the budget isn't already there.
	 */
	public void update(LineInterface line)
	{
		// If created already (it is not in the create hamper)
		if (this.id != -1)
		{
			// If BudgetUpdates hamper does not contain the budget already
			if (!Hampers.getBudgetUpdates().containsKey(this.id))
			{
				Hampers.getBudgetUpdates().put(this.id, this);
			}
		}
	}

	@Override
	/**
	 * Adds the budget to the update hamper if the budget isn't already there.
	 */
	public void update(NoteInterface note)
	{
		// If created already (it is not in the create hamper)
		if (this.id != -1)
		{
			// If BudgetUpdates hamper does not contain the budget already
			if (!Hampers.getBudgetUpdates().containsKey(this.id))
			{
				Hampers.getBudgetUpdates().put(this.id, this);
			}
		}
	}
}